package qiniu

import (
	"context"
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/service/attachment/model"
	"mall/service/attachment/rpc/internal/svc"
	"mall/service/attachment/rpc/internal/utils/errorhandler"
	"mall/service/attachment/rpc/types/attachment"
	"mall/service/backend/common/errorx"
	"mall/service/backend/common/i18n"

	"github.com/qiniu/go-sdk/v7/auth/qbox"
	"github.com/qiniu/go-sdk/v7/storage"
	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type QiniuWatermarkLogic struct {
	ctx           context.Context
	svcCtx        *svc.ServiceContext
	bucketManager *storage.BucketManager
	bucket        string
	config        *storage.Config
	logx.Logger
}

func NewQiniuWatermarkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QiniuWatermarkLogic {
	return &QiniuWatermarkLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

// 持久化数据处理 - 生成缩略图
func (l *QiniuWatermarkLogic) QiniuWatermark(in *attachment.RecordInfo) (*attachment.BaseResp, error) {
	mac := qbox.NewMac(l.svcCtx.Config.QiniuConf.AccessKey, l.svcCtx.Config.QiniuConf.SecretKey)

	cfg := &storage.Config{
		UseHTTPS:      l.svcCtx.Config.QiniuConf.UseHTTPS,      // 是否使用https域名进行资源管理
		UseCdnDomains: l.svcCtx.Config.QiniuConf.UseCdnDomains, // 上传是否使用CDN上传加速
	}

	// 指定空间所在的区域，如果不指定将自动探测
	// 如果没有特殊需求，默认不需要指定
	// cfg.Region=&storage.ZoneHuabei
	// switch l.svcCtx.Config.QiniuConf.Region {
	// case "z2":
	// 	cfg.Region = &storage.ZoneHuanan
	// }

	l.bucketManager = storage.NewBucketManager(mac, cfg)
	l.bucket = l.svcCtx.Config.QiniuConf.Bucket
	l.config = cfg

	file, err := l.findAttachment(in)
	if err != nil {
		return nil, err
	}

	if file == nil {
		return nil, errorx.NewNotFoundError(i18n.TargetNotFound)
	}

	if len(file.GUID) == 0 {
		return nil, errorx.NewInternalError("file.wrongKey")
	}

	info, err := l.findInfo(file.ID, strings.TrimLeft(file.GUID, "/"), 0)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if info == nil {
		return nil, errorx.NewNotFoundError(i18n.TargetNotFound)
	}

	if len(info.Bucket) > 0 {
		l.bucket = info.Bucket
	}

	var encodedKodoScheme string
	if in.FileId != nil && *in.FileId > 0 {
		watermark, err := l.svcCtx.MediaFile.FindOneNoCache(l.ctx, nil, *in.FileId)
		if err != nil {
			logx.Errorw("QiniuWatermark failed to find the specific watermark", logx.Field("detail", err.Error()))
		} else {
			watermarkInfo, err := l.findInfo(watermark.ID, strings.TrimLeft(watermark.GUID, "/"), 0)
			if err != nil {
				logx.Errorw("QiniuWatermark failed to find the specific watermark info", logx.Field("detail", err.Error()))
			} else if watermarkInfo == nil {
				logx.Errorw("QiniuWatermark failed to find the specific watermark info, not found")
			} else if watermarkInfo.Bucket != l.bucket {
				logx.Errorw("QiniuWatermark failed to find the specific watermark info, different bucket")
			} else {
				encodedKodoScheme = l.encodedKodoEntry(watermarkInfo.KeyValue)
			}
		}
	}
	if len(encodedKodoScheme) == 0 {
		encodedKodoScheme = l.encodedKodoEntry("ets/image/scentrobe/logo-h.png")
		logrus.Info(fmt.Sprintf("QiniuWatermark encodedKodoScheme: %+v", encodedKodoScheme))

		// encodedKodoScheme = "a29kbzovL2R1amlhb3hpdS9ldHMvaW1hZ2Uvc2NlbnRyb2JlL2xvZ28taC5wbmc="
	}

	dotIndex := strings.LastIndex(file.GUID, ".")
	slashIndex := strings.LastIndex(file.GUID, "/")
	var keyName string
	var keySuffix string
	if dotIndex == -1 || dotIndex < slashIndex {
		keyName = strings.TrimLeft(file.GUID, "/")
	} else {
		keyName = strings.TrimLeft(file.GUID[:dotIndex], "/")
		keySuffix = file.GUID[dotIndex:]
	}

	var style string
	if in.Style != nil && len(strings.TrimSpace(*in.Style)) > 0 {
		style = strings.TrimSpace(*in.Style)
	}

	// 使用数据处理，首先需要构建一个OperationManager对象
	operationManager := storage.NewOperationManager(mac, l.config)

	var mode int = 1                 // 图片水印
	var dissolve int = 70            // 透明度，取值范围1-100，默认值为100（完全不透明）。
	var gravity string = "NorthWest" // 水印位置，参考水印锚点参数表，默认值为SouthEast（右下角）。
	var dx int = 10                  // 横轴边距，单位:像素(px)，默认值为10。
	var dy int = 10                  // 纵轴边距，单位:像素(px)，默认值为10。
	var ws string = "0.08"           // 水印图片自适应原图的比例，ws的取值范围为0-1。具体是指水印图片保持原比例，按照自适应原图的类型wst，比如默认是按照短边，则水印图片短边=原图短边＊ws。
	// var wst int = 0 // 水印图片自适应原图的类型，取值0、1、2、3分别表示为自适应原图的短边、长边、宽、高，默认值为0。
	var fopWatermark string
	var watermarkKey string
	switch style {
	default:
		//https://dora-doc.qiniu.com/gogopher.jpg?watermark/1/image/a29kbzovL2RldmVsb3Blci1kb2N1bWVudHMtaW1hZ2UvcWluaXV5dW4ucG5n/dissolve/50/gravity/SouthEast/dx/20/dy/20/ws/0.2
		fopWatermark = fmt.Sprintf("watermark/%d/image/%s/dissolve/%d/gravity/%s/dx/%d/dy/%d/ws/%s",
			mode, encodedKodoScheme, dissolve, gravity, dx, dy, ws)
		// /wst/%d
		watermarkKey = fmt.Sprintf("%s_wm%s", keyName, keySuffix)
	}

	// logrus.Info(fmt.Sprintf("Qiniu Pfop fopWatermark: %s", fopWatermark))

	var encodedEntryURI string = storage.EncodedEntry(l.bucket, watermarkKey)

	var fopBatch []string
	fopImageView := fmt.Sprintf("%s|saveas/%s", fopWatermark, encodedEntryURI)
	fopBatch = append(fopBatch, fopImageView)

	fops := strings.Join(fopBatch, ";")
	// 强制重新执行数据处理任务
	force := true
	// 数据处理指令全部完成之后，通知该地址
	notifyURL := l.svcCtx.Config.QiniuConf.NotifyURL
	// notifyURL := ""
	// 数据处理的私有队列，必须指定以保障处理速度
	pipeline := "scentrobe_private"
	persistentId, err := operationManager.Pfop(l.bucket, strings.TrimLeft(file.GUID, "/"), fops, pipeline, notifyURL, force)
	if err != nil {
		logrus.Info(fmt.Sprintf("QiniuWatermark Qiniu Pfop err: %+v", err))
		// Post "https://api-z2.qiniu.com/pfop/": dial tcp [240e:ff:d131:100:3::3ea]:443: connect: cannot assign requested address
		return nil, errorx.NewInternalError("file.updateFileFailed")
	}

	// logrus.Info(fmt.Sprintf("Qiniu Pfop persistentId: %+v", persistentId))
	// Qiniu Pfop persistentId: z2.01z211cwe14h24zxvs00mv2nrn000d7n

	watermarkInfo, err := l.findInfo(file.ID, watermarkKey, info.ID)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if watermarkInfo != nil {
		watermarkInfo.PersistentID = persistentId
		err = l.svcCtx.FileInfo.Update(l.ctx, nil, watermarkInfo)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}
	} else {
		watermarkInfo = &model.AttachmentFileInfo{}
		watermarkInfo.AttachmentID = file.ID
		watermarkInfo.ParentID = info.ID
		watermarkInfo.KeyValue = watermarkKey
		watermarkInfo.PersistentID = persistentId
		watermarkInfo.FileType = uint8(globalkey.AttachmentTypeWatermark)
		err = l.svcCtx.FileInfo.Insert(l.ctx, nil, watermarkInfo)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}
	}

	newType := l.getAttachmentType(int32(file.PostType), "watermark")
	if newType > 0 {
		file.PostType = uint16(newType)
		err = l.svcCtx.MediaFile.Update(l.ctx, nil, file, nil)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}
	}

	return &attachment.BaseResp{}, nil
}

func (l *QiniuWatermarkLogic) findAttachment(in *attachment.RecordInfo) (*model.Attachment, error) {
	if in.Id != nil && len(strings.TrimSpace(*in.Id)) > 0 {
		if i, err := strconv.ParseInt(strings.TrimSpace(*in.Id), 10, 64); err == nil && i > 0 {
			res, err := l.svcCtx.MediaFile.FindOneNoCache(l.ctx, nil, uint64(i))
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			return res, nil
		}
	}

	return nil, errorx.NewInvalidArgumentError(i18n.ValidationError)
}

func (l *QiniuWatermarkLogic) findInfo(id uint64, key string, pid uint64) (*model.AttachmentFileInfo, error) {
	filterMap := make(map[string]map[string][]any)
	filterMap[model.AttachmentFileInfoColumns.KeyValue] = map[string][]any{
		"equalTo": {key},
	}
	filterMap[model.AttachmentFileInfoColumns.ParentID] = map[string][]any{
		"equalTo": {pid},
	}
	filterMap[model.AttachmentFileInfoColumns.DelStatus] = map[string][]any{
		"notEqualTo": {globalkey.StatusDeleted},
	}
	list, count, err := l.svcCtx.FileInfo.GetList(l.ctx, model.M{
		"pageSize": uint64(1),
		"orderBy":  model.AttachmentFileInfoColumns.ID + " DESC",
	}, 1, filterMap)
	if err != nil {
		return nil, err
	}

	var info *model.AttachmentFileInfo
	if count > 1 { // 记录重复异常
		info = list[0]

		var metaVals []string
		for _, file := range list {
			metaVals = append(metaVals, strconv.FormatUint(file.ID, 10))
		}

		var log model.AttachmentFileInfoLog
		log.AttachmentID = id
		log.InfoID = info.ID
		log.Type = uint8(globalkey.AttachmentLogInfoDuplicated)
		log.KeyValue = key
		log.Count = uint64(count)
		log.MetaValue = strings.Join(metaVals, ",")
		err = l.svcCtx.FileInfo.InsertLog(l.ctx, nil, &log)
		if err != nil {
			return nil, err
		}

		return info, nil
	}

	if count == 1 {
		info = list[0]

		if info.AttachmentID > 0 && info.AttachmentID != id { // 记录重复异常
			var log model.AttachmentFileInfoLog
			log.AttachmentID = id
			log.InfoID = info.ID
			log.Type = uint8(globalkey.AttachmentLogDuplicated)
			log.KeyValue = key
			log.Count = uint64(count)
			err = l.svcCtx.FileInfo.InsertLog(l.ctx, nil, &log)
			if err != nil {
				return nil, err
			}
		}

		return info, nil
	}

	return nil, nil
}

func (l *QiniuWatermarkLogic) getAttachmentType(fileType int32, op string) int32 {
	var newFileType int32
	switch op {
	case "put":
		switch fileType {
		case globalkey.AttachmentTypePutPendingFile:
			newFileType = globalkey.AttachmentTypePutReadyFile
		case globalkey.AttachmentTypeWatermarkPendingFile:
			newFileType = globalkey.AttachmentTypeWatermarkPendingPutReadyFile
		case globalkey.AttachmentTypeThumbnailPendingFile:
			newFileType = globalkey.AttachmentTypeThumbnailPendingPutReadyFile
		case globalkey.AttachmentTypePendingFile:
			newFileType = globalkey.AttachmentTypePendingPutReadyFile
		default:
			newFileType = globalkey.AttachmentTypePutReadyFile
		}
	case "thumbnail":
		switch fileType {
		case globalkey.AttachmentTypeThumbnailPendingPutReadyFile:
			newFileType = globalkey.AttachmentTypeThumbnailReadyFile
		case globalkey.AttachmentTypePendingWatermarkReadyFile:
			newFileType = globalkey.AttachmentTypeReadyFile
		default:
			newFileType = globalkey.AttachmentTypeThumbnailReadyFile
		}
	case "watermark":
		switch fileType {
		case globalkey.AttachmentTypeWatermarkPendingPutReadyFile:
			newFileType = globalkey.AttachmentTypeWatermarkReadyFile
		case globalkey.AttachmentTypePendingPutReadyFile:
			newFileType = globalkey.AttachmentTypePendingWatermarkReadyFile
		default:
			newFileType = globalkey.AttachmentTypeWatermarkReadyFile
		}
	}

	return newFileType
}

func (l *QiniuWatermarkLogic) encodedKodoEntry(key string) string {
	entry := fmt.Sprintf("kodo://%s/%s", l.bucket, key)
	return base64.URLEncoding.EncodeToString([]byte(entry))
}
